package com.shenda.device.codec;

import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import com.shenda.device.message.RespMsg;
import com.swak.rxtx.channel.Channel;
import com.swak.rxtx.codec.ByteToMessageDecoder;
import com.swak.rxtx.codec.Encodes;
import com.swak.utils.Ints;

import io.netty.buffer.ByteBuf;

/**
 * 命令解析
 * 
 * @author lifeng
 */
public class CommandDecoder extends ByteToMessageDecoder implements Encodes {

	// 命令格式
	private final int cmdLengthOffset = 0;
	private final int cmdLength = 4;
	private final int cmdLengthEndOffset = cmdLengthOffset + cmdLength;

	private AtomicLong size = new AtomicLong();

	@Override
	protected void decode(Channel channel, ByteBuf in, List<Object> out) throws Exception {
		Object decoded = decode(channel, in);
		if (decoded != null) {
			out.add(decoded);
		}
	}

	protected Object decode(Channel channel, ByteBuf in) throws Exception {

		// 读取命令
		String cmd = this.readCmd(in);

		// 对应的命令
		RespMsg respMsg = RespMsg.of(cmd);

		// 整个命令的长度
		int frameLength = respMsg.commandLength();

		// 如果是未知的命令则跳过
		if (frameLength <= 0) {
			in.skipBytes(Ints.min(in.readableBytes(), cmdLengthEndOffset));
			if (logger.isDebugEnabled()) {
				logger.debug("命令解码错误，需跳过字节" + size.addAndGet(cmdLengthEndOffset));
			}
			return null;
		}

		// 如果命令不够解析则等待后续的处理
		if (in.readableBytes() < frameLength) {
			return null;
		}

		// 返回真实的命令
		return respMsg.parse(this.readData(in, frameLength));
	}

	/**
	 * 读取命令
	 * 
	 * @param in
	 * @return
	 */
	private String readCmd(ByteBuf in) {

		// 至少长度
		if (in.readableBytes() < cmdLengthEndOffset) {
			return null;
		}

		// 实际的命令字段的长度
		int offset = in.readerIndex() + cmdLengthOffset;

		// head + cmd
		int cmd = in.getInt(offset);

		// 得到命令字符
		return Integer.toHexString(cmd).toUpperCase();
	}

	/**
	 * 读取整个数据
	 * 
	 * @param in
	 * @param frameLength
	 * @return
	 */
	private byte[] readData(ByteBuf in, int frameLength) {
		int readerIndex = in.readerIndex();
		ByteBuf frame = in.retainedSlice(readerIndex, frameLength);
		in.readerIndex(readerIndex + frameLength);

		try {
			// 数据长度
			byte[] data = new byte[frame.readableBytes()];
			frame.readBytes(data);

			// 格式化为字符串
			return data;
		} finally {
			frame.release();
		}
	}
}
